import json

import requests
import re
import retry


class Gitee_Option:
    def __init__(self, gitee_url, token=None):
        """
        :param gitee_url: 仓库链接，例如：https://gitee.com/zl_b/file_config.git
        :param token: Gitee 个人访问令牌
        """
        pattern = r'https://gitee.com/([^/]+)/([^/]+)(\.git)?'
        match = re.match(pattern, gitee_url)
        if not match:
            raise ValueError("输入的 Gitee 仓库链接格式不正确，请提供有效的链接。")
        self.owner, self.repo = match.group(1), match.group(2).replace('.git', '')
        self.base_url = f"https://gitee.com/{self.owner}/{self.repo}"
        self.api_base_url = f"https://gitee.com/api/v5/repos/{self.owner}/{self.repo}"
        self.token = token

    def convert_to_raw_url(self, url):
        """
        将 Gitee 仓库文件的 URL 中的 'blob' 替换为 'raw'
        :param url: Gitee 仓库文件的 URL
        :return: 转换后的 raw URL
        """
        return url.replace('/blob', '/raw')

    def _make_request(self, url, timeout=10):
        """
        封装请求逻辑，统一处理请求异常
        :param url: 请求的 URL
        :param timeout: 请求超时时间
        :return: 请求响应对象，如果请求失败返回 None
        """
        try:
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
            }
            if self.token:
                headers['Authorization'] = f'token {self.token}'

            response = requests.get(url, headers=headers, timeout=timeout)
            response.raise_for_status()
            return response

        except requests.HTTPError as http_err:
            print(f"HTTP 错误: {http_err}，请求的 URL: {url}")
        except requests.RequestException as req_err:
            print(f"请求出错: {req_err}，请求的 URL: {url}")
        return None

    @retry.retry(Exception, tries=3, delay=3, backoff=2)
    def get_gitee_repo_file(self, url, file_path=""):
        """
        读取指定 Gitee 仓库文件内容
        :param url: Gitee 仓库文件的 URL
        :param file_path: 保存的路径
        :return: 文件内容，如果请求失败则返回 None
        """
        if not isinstance(url, str) or not isinstance(file_path, str):
            raise ValueError("URL 和文件保存路径必须为字符串类型")
        raw_url = self.convert_to_raw_url(url)
        response = self._make_request(raw_url)
        # print(response)
        if response:
            content = response.text
            if file_path:
                self.save_file(content, file_path)
            # print(content)
            return content
        return None

    def save_file(self, content, file_path):
        """
        将内容保存到本地文件
        :param content: 要保存的内容
        :param file_path: 保存的文件路径
        :return: 保存成功返回 True，失败返回 False
        """
        if not isinstance(content, str) or not isinstance(file_path, str):
            raise ValueError("内容和文件路径必须为字符串类型")
        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(content)
            print(f"文件已成功保存到 {file_path}")
            return True
        except IOError as io_err:
            print(f"保存文件时出错: {io_err}")
            return False

    @retry.retry(Exception, tries=3, delay=3, backoff=2)
    def list_all_files_and_folders(self, path=""):
        """
        递归列出指定 Gitee 仓库中的所有文件和文件夹
        :param path: 仓库内的路径，默认为根目录
        :return: 包含文件和文件夹信息的字典
        """
        if not isinstance(path, str):
            raise ValueError("路径必须为字符串类型")
        url = f"{self.api_base_url}/contents/{path}"
        response = self._make_request(url)
        if response:
            items = response.json()
            result = {}
            for item in items:
                if item["type"] == "file":
                    result[item["name"]] = item["download_url"]
                elif item["type"] == "dir":
                    result[item["name"]] = self.list_all_files_and_folders(item["path"])
            return result
        return {}

    @retry.retry(Exception, tries=3, delay=10, backoff=10)
    def download_file(self, web_path, save_path):
        """
        下载指定文件
        :param web_path: 网页路径，例如：下载/1.exe
        :param save_path: 保存路径
        :return: 下载成功返回 True，失败返回 False
        """
        if not isinstance(web_path, str) or not isinstance(save_path, str):
            raise ValueError("网页路径和保存路径必须为字符串类型")
        url = f"{self.api_base_url}/contents/{web_path}"
        response = self._make_request(url)
        if response:
            data = response.json()
            # print(data)
            try:
                # print(data["download_url"])
                download_url = data["download_url"]
            except KeyError:
                print(f"未找到下载链接，URL: {url}")
                return False
            file_response = self._make_request(download_url)
            if file_response:
                with open(save_path, 'wb') as f:
                    f.write(file_response.content)
                # print(f"文件 {web_path} 已下载到 {save_path}")
                return True
        return False

    @staticmethod
    @retry.retry(Exception, tries=3, delay=10, backoff=10)
    def download_file_cookies(url, save_path, cookies_file=None):
        '''
        cookies_file = 'xx.json'
        '''
        if cookies_file != None:
            # 从 JSON 文件中读取 cookies
            try:
                with open(cookies_file, 'r', encoding='utf-8') as f:
                    cookies = json.load(f)
            except FileNotFoundError:
                print(f"错误：找不到 cookies 文件 '{cookies_file}'")
                return False
            except json.JSONDecodeError:
                print(f"错误：文件 '{cookies_file}' 不是有效的 JSON 格式")
                return False

            # 将列表形式的 cookies 转换为字典形式
            cookie_dict = {cookie['name']: cookie['value'] for cookie in cookies}
            try:
                response = requests.get(url, cookies=cookie_dict, stream=True)
                response.raise_for_status()
                with open(save_path, 'wb') as file:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            file.write(chunk)
                # print(f"文件下载成功，保存路径: {save_path}")
                return True
            except requests.RequestException as e:
                # print(f"下载文件时出错: {e}")
                return False
        else:
            try:
                response = requests.get(url, stream=True)
                response.raise_for_status()
                with open(save_path, 'wb') as file:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            file.write(chunk)
                # print(f"文件下载成功，保存路径: {save_path}")
                return True
            except requests.RequestException as e:
                # print(f"下载文件时出错: {e}")
                return False
